#include "head.h"
#include <func.h>
int exitPipe[2];
void sigFunc(int signum){
    puts("signal is coming!");
    write(exitPipe[1], "1", 1);
}

int main(int argc, char *argv[]){
    // ./server 192.168.31.128 1234 3
    ARGS_CHECK(argc, 4);
    openlog(NULL,LOG_PID,LOG_USER);
    pipe(exitPipe);
    if(fork() != 0){
        close(exitPipe[0]);//close read pipe
        signal(SIGQUIT, sigFunc);
        wait(NULL);
        // //清空UserLoginInformation表的token列
        // MYSQL *db = mysql_init(NULL);
        // initDataBase(db);
        // setTokenNull(db);
        // mysql_close(db);
        exit(0);
    }
    close(exitPipe[1]);//close write pipe
    int workerNum = atoi(argv[3]);
    threadPool_t threadPool;
    threadPoolInit(&threadPool, workerNum);
    int sockFd;
    makeWorker(&threadPool);
    tcpInit(argv[1], argv[2], &sockFd);
    int epFd = epoll_create(10);
    epollAdd(sockFd, epFd);
    epollAdd(exitPipe[0], epFd);
    struct epoll_event readyList[10];

    MYSQL *db = mysql_init(NULL);
    initDataBase(db);

    //每次打开服务器都生成新的盐值
    char salt[50] = {0};
    getSalt(salt);
    int NetFd = -1;

    while(1){
        int readyNum = epoll_wait(epFd, readyList, 10, -1);
        puts("epoll ready");
        for(int i=0; i<readyNum; ++i){
            if(readyList[i].data.fd == sockFd){
                int netFd = accept(sockFd, NULL, NULL);
                // printf("netfd = %d\n", netFd);
                
                char user_Name[100] = {0};
                int length;
                recv(netFd, &length, sizeof(length), 0);
                // printf("%d\n", dataLength);
                recv(netFd, user_Name, length, 0);

                char  type;
                recv(netFd, &type, 1, 0);
                 
                char token[100] = {0};
                makeToken(salt, user_Name, token);//生成token
/**********************************************************************************************************/
                if(type == '0' || type == '1'){                
                    if(type == '0'){//新用户注册登录
                        char choice;
                        recv(netFd, &choice, 1, 0);
                        //choice为1表示登录
                        //choice为2表示注册
                        
                        //无论登录还是注册都需要输入密码
                        int dataLength;
                        recv(netFd, &dataLength, sizeof(dataLength), 0);
                        // printf("%d\n", dataLength);
                        char password[1024] = {0};
                        recv(netFd, password, dataLength, 0);
                        
                        //strcpy(user_Name, userName);
                        
                        
                        if(choice == '1'){//登录
                            int isLoginUsrLegal = usrLogin(db, user_Name, password);
                            send(netFd, &isLoginUsrLegal, 1, MSG_NOSIGNAL);
                            if(isLoginUsrLegal == 1 || isLoginUsrLegal == 2){//密码错误
                                close(netFd);
                                continue;
                            }
                            //如果用户登录信息正确，token存入数据库，并且给客户端发送token值
        
                            //该用户为合法用户，但尚未在本地登录，需要向数据库存入其token值
                            // char file_name[255] = {0};
                            // sprintf(file_name, "/%s", userName);
                            //insertTokenToUserInformation(db, userName, token);
                        }
                        else if(choice == '2'){//注册
                            int isRegisterRight = usrRegister(db, user_Name, password);
                            if(isRegisterRight == 0){//注册需要生成token并插表
                                send(netFd, "0", 1, MSG_NOSIGNAL);
                                insertCatalogWhenRegister(db, user_Name);
                            }
                            else{
                                send(netFd, "1", 1, MSG_NOSIGNAL);
                                close(netFd);
                                continue;
                            }
                        }
                        train_t token_train;
                        bzero(&token_train, sizeof(token_train));
                        token_train.dataLength = strlen(token);
                        strcpy(token_train.buf, token);
                        int ret = send(netFd, &token_train, sizeof(token_train.dataLength)+token_train.dataLength, MSG_NOSIGNAL);
                        ERROR_CHECK(ret, -1, "send token");//向客户端发送当前token                    
                    }
/**********************************************************************************************************/                
                    else if(type == '1'){//旧用户登录
                        //4.0版本
                        //通过用户名和发送来的token与数据库匹配
                        //三种可能
                        //1.数据库token==NULL，旧用户信息过期，需要判断用户是否合法
                        //2.token不匹配，为非法用户
                        //3.token匹配--->合法用户
                        //4.1版本更改后非法用户和旧用户信息过期无法进行区分，故分两种情况讨论
                        //1.token过期/非法----->token不匹配，用户重新登录
                        //2.token匹配----->直接进入
                        int length;
                        char usrToken[100] = {0};
                        recv(netFd, &length, sizeof(int), 0);
                        recv(netFd, usrToken, length, 0);
                    
                        if(strcmp(token, usrToken) != 0){//token不匹配，用户重新登录
                            send(netFd, "1", 1, MSG_NOSIGNAL);
                            //重新验证用户信息
                            int dataLength;
                            recv(netFd, &dataLength, sizeof(dataLength), 0);
                            // printf("%d\n", dataLength);
                            char password[1024] = {0};
                            recv(netFd, password, dataLength, 0);
                            
                            int isLoginUsrLegal = usrLogin(db, user_Name, password);
                            send(netFd, &isLoginUsrLegal, 1, MSG_NOSIGNAL);
                            if(isLoginUsrLegal == 1 || isLoginUsrLegal == 2){//密码错误
                                close(netFd);
                                continue;
                            }
                            //如果用户登录信息正确，token存入数据库，并且给客户端发送token值
                            //该用户为合法用户，在其文件夹写入新的token
                            //insertTokenToUserInformation(db, userName, token);
                            train_t token_train;
                            bzero(&token_train, sizeof(token_train));
                            token_train.dataLength = strlen(token);
                            strcpy(token_train.buf, token);
                            int ret = send(netFd, &token_train, sizeof(token_train.dataLength)+token_train.dataLength, MSG_NOSIGNAL);
                            ERROR_CHECK(ret, -1, "send token");//向客户端发送当前token
                        }
                        else{//token匹配
                            send(netFd, "0", 1, MSG_NOSIGNAL);
                        }
                    }
                    //合法用户跳转至此部分；
                    //记录用户上线
                    time_t ttime;
                    time(&ttime);
                    struct tm *pTm = gmtime(&ttime);
                    syslog(LOG_INFO, "%s %04d:%02d:%02d", user_Name, (1900 + pTm->tm_year), (1 + pTm->tm_mon), (pTm->tm_mday));
                    //此部分应当发送/usr_name给客户端
                    char first_file_name[100] = {0};
                    sprintf(first_file_name, "/%s", user_Name);
                    train_t t;
                    bzero(&t, sizeof(train_t));
                    t.dataLength = strlen(first_file_name);
                    strcpy(t.buf, first_file_name);
                    send(netFd, &t, sizeof(t.dataLength) + t.dataLength, MSG_NOSIGNAL);
                    //将netFd加入监听
                    epollAdd(netFd, epFd);
                    //NetFd = netFd;                    
                }
/*用户登录*/
/**********************************************************************************************************/
/*长命令分支*/
                else if(type == '2'){//长命令 
                    //判断是否为合法用户，若为合法用户，就将netFd和user_Name加入任务队列
                    //通过token + user_Name进行判断
                    //客户端方在通过长命令connect sockFd后，应先发送一个user_name
                    //再发送一个user_token
                    puts("Handle long event");
                    int length;
                    char usrToken[100] = {0};
                    recv(netFd, &length, sizeof(int), 0);
                    recv(netFd, usrToken, length, 0);
                    printf("usrToken = %s\n", usrToken);
                    length = 0;
                    char curPath[100] = {0};
                    recv(netFd, &length, sizeof(int), 0);
                    recv(netFd, curPath, length, 0);
                    printf("curPath = %s\n", curPath);
                    if(strcmp(usrToken, token) != 0){
                        send(netFd, "1", 1, MSG_NOSIGNAL);
                        close(netFd);
                        continue;
                    }
                    else{
                        send(netFd, "0", 1, MSG_NOSIGNAL);
                    }

                    pthread_mutex_lock(&threadPool.mutex);
                    enUserQueue(&threadPool.userQueue, netFd, user_Name, curPath);
                    printf("New task for %s\n", user_Name);
                    pthread_cond_signal(&threadPool.cond);
                    pthread_mutex_unlock(&threadPool.mutex);

                    // train_t t;
                    // //sprintf(t.buf, "%s$", curPath);
                    // strcpy(t.buf, curPath);
                    // t.dataLength = strlen(t.buf);
                    // // printf("size = %d, bashPre = %s\n", t.dataLength, t.buf);
                    // send(NetFd, &t, sizeof(t.dataLength) + t.dataLength, MSG_NOSIGNAL);        
                }            
            }
            else if(readyList[i].data.fd == exitPipe[0]){
                // for(int j=0; j<workerNum; j++){
                //     pthread_cancel(threadPool.tidArr[j]);
                // }
                puts("I am going to die!");
                threadPool.isThreadPoolRunning = 0;
                pthread_cond_broadcast(&threadPool.cond);
                for(int j=0; j<workerNum; ++j){
                    pthread_join(threadPool.tidArr[j], NULL);
                }
                puts("exit");
                exit(0);
            }
            else{//与短命令进行的交互
                //进行交互前先要得到当前用户的用户名和其pwd
                NetFd = readyList[i].data.fd;                
                int dataLength;
                char usr_name[100] = {0};
                int ret = recv(NetFd, &dataLength, sizeof(int), 0);
                if(ret == 0){
                    epollDel(NetFd, epFd);
                    close(NetFd);
                    continue;
                }
                recv(NetFd, usr_name, dataLength, 0);

                dataLength = 0;
                char curPath[1024] = {0};
                recv(NetFd, &dataLength, sizeof(int), 0);
                //printf("datalength = %d\n", dataLength);
                recv(NetFd, curPath, dataLength, 0);
                printf("curPath = %s\n", curPath);

                MYSQL *db = mysql_init(NULL);
                initDataBase(db);

                puts("Handle Short Event");
                dataLength = 0;
                char commandLine[1024] = {0};
                ret = recv(NetFd, &dataLength, sizeof(dataLength), MSG_WAITALL);
                if(ret == 0){
                    continue;
                }
                recv(NetFd, commandLine, dataLength, MSG_WAITALL);
                // printf("length = %d, line = %s\n", dataLength, commandLine);
                // char arr[200] = "";
                // strcpy(arr, commandLine);
                // char *command = strtok(arr, " ");
                char command[100] = {0};
                getCommand(commandLine, command);
                // printf("%s\n", command);
                if(strcmp(command, "exit") == 0){
                    epollDel(NetFd, epFd);
                    close(NetFd);
                    continue;
                }
                else if(strcmp(command, "cd") == 0){
                    //syslog(LOG_INFO, "cd");
                    cd(db, commandLine, curPath, usr_name, NetFd);
                    printf("cd :curPath = %s\n", curPath);
                }
                else if(strcmp(command, "ls") == 0){
                    //syslog(LOG_INFO, "ls");
                    ls(db, curPath, NetFd);
                }
                else if(strcmp(command, "rm") == 0){
                    //syslog(LOG_INFO, "remove");
                    rm(db, commandLine, curPath, usr_name, NetFd);
                }
                else if(strcmp(command, "pwd") == 0){
                    //syslog(LOG_INFO, "pwd");
                    pwd(curPath, NetFd);
                }
                else if(strcmp(command, "mkdir") == 0){
                    //syslog(LOG_INFO, "mkdir");
                    makedir(db, commandLine, curPath, usr_name, NetFd);
                }
                else if(strcmp(command, "rmdir") == 0){
                    //syslog(LOG_INFO, "rmdir");
                    removedir(db, commandLine, curPath, usr_name, NetFd);
                }
                else{

                }

                syslog(LOG_INFO, "%s", commandLine);
                train_t t;
                //sprintf(t.buf, "%s$", curPath);
                printf("curPath = %s\n", curPath);
                strcpy(t.buf, curPath);
                t.dataLength = strlen(t.buf);
                // printf("size = %d, bashPre = %s\n", t.dataLength, t.buf);
                send(NetFd, &t, sizeof(t.dataLength) + t.dataLength, MSG_NOSIGNAL);        
            }            
        }
    }
    closelog();
}

